package com.platform.core.sms;

import com.platform.comm.util.StringUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.codec.binary.Base64;

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

@Log4j2
public class SHA256withRSA {

    public static final String KEY_ALGORITHM = "RSA";
    public static final String PUBLIC_KEY = "publicKey";
    public static final String PRIVATE_KEY = "privateKey";
    public static final int KEY_SIZE = 2048;
    public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";

    /**
     * 生成密钥对字符串
     *
     * @return 映射
     */
    public static Map<String, String> generateKeyBytes() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            keyPairGenerator.initialize(KEY_SIZE);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            Map<String, String> keyMap = new HashMap<>(10);
            keyMap.put(PUBLIC_KEY, Base64.encodeBase64String(publicKey.getEncoded()));
            keyMap.put(PRIVATE_KEY, Base64.encodeBase64String(privateKey.getEncoded()));
            return keyMap;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            log.error("签名验证失败");

        }
        return null;
    }

    /**
     * 还原公钥
     *
     * @param publicKey 公钥字符串
     * @return 公钥
     */
    public static PublicKey restorePublicKey(String publicKey) {
        byte[] priKeyByte;
        PublicKey pubTypeKey;
        try {
            priKeyByte = Base64.decodeBase64(publicKey.getBytes(StandardCharsets.UTF_8));
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(priKeyByte);
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
            pubTypeKey = factory.generatePublic(x509EncodedKeySpec);
            return pubTypeKey;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            log.error("签名算法失败，不支持的签名算法");
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
            log.error("签名算法失败，不支持的密钥");
        }
        return null;
    }

    /**
     * 还原私钥
     *
     * @param privateKey 私钥字符串
     * @return 私钥
     */
    public static PrivateKey restorePrivateKey(String privateKey) {
        byte[] priKeyByte;
        PrivateKey priTypeKey;
        try {
            priKeyByte = Base64.decodeBase64(privateKey.getBytes(StandardCharsets.UTF_8));
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(priKeyByte);
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
            priTypeKey = factory.generatePrivate(pkcs8EncodedKeySpec);
            return priTypeKey;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            log.error("签名算法失败，不支持的签名算法");
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
            log.error("签名算法失败，不支持的密钥");
        }
        return null;
    }


    /**
     * 签名
     *
     * @param privateKey 私钥
     * @param plainText  明文
     * @return 签名信息
     */
    public static String sign(String privateKey, String plainText) {
        return Base64.encodeBase64String(sign(restorePrivateKey(privateKey), plainText));
    }


    /**
     * 签名
     *
     * @param privateKey 私钥
     * @param plainText  明文
     * @return 字节数组
     */
    public static byte[] sign(PrivateKey privateKey, String plainText) {
        byte[] signed = null;
        try {
            Signature sign = Signature.getInstance(SIGNATURE_ALGORITHM);
            sign.initSign(privateKey);
            sign.update(plainText.getBytes());
            signed = sign.sign();
        } catch (NoSuchAlgorithmException |
                InvalidKeyException |
                SignatureException e) {
            e.printStackTrace();
        }
        return signed;
    }

    /**
     * 验签
     *
     * @param publicKey 公钥
     * @param srcMsg    明文
     * @param signed    签名
     */
    public static boolean verifySign(String publicKey, String srcMsg, String signed) {
        PublicKey publicTypeKey = restorePublicKey(publicKey);
        return verifySign(publicTypeKey, srcMsg, signed);

    }

    /**
     * 验签
     *
     * @param publicKey 公钥
     * @param plainText 明文
     * @param signed    签名
     */
    private static boolean verifySign(PublicKey publicKey, String plainText, String signed) {
        boolean signedSuccess = false;
        try {
            Signature verifySign = Signature.getInstance(SIGNATURE_ALGORITHM);
            verifySign.initVerify(publicKey);
            verifySign.update(plainText.getBytes(StandardCharsets.UTF_8));
            signedSuccess = verifySign.verify(Base64.decodeBase64(signed.getBytes(StandardCharsets.UTF_8)));
        } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
            e.printStackTrace();
        }
        return signedSuccess;
    }

    /**
     * bytes[]换成16进制字符串
     *
     * @param src 源字节数组
     * @return 16进制字符串
     */
    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder();
        if (src == null || src.length <= 0) {
            return null;
        }
        for (byte b : src) {
            int v = b & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     * 十六进制的字符串转换为字节数组
     *
     * @param hexString 16进制字符串
     * @return 字节数组
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (StringUtil.isEmpty(hexString)) {
            return null;
        }
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];

        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = chartobyte(hexChars[pos]);
        }
        return d;
    }

    private static byte chartobyte(char c) {
        return (byte) "123456789abcdef".indexOf(c);
    }

    public static void main(String[] args) {
        Map<String, String> map = generateKeyBytes();
        assert map != null;
        System.out.println("publicKey=" + map.get("publicKey"));
        System.out.println("privateKey=" + map.get("privateKey"));
    }

}
